/**
 * Copyright (c) 2010-2011 Pierre Labatut
 *
 * This file is part of Space-Chronicles project.
 *
 * Space-Chronicles is free software: you can redistribute it and/or modify it
 * under the terms of its license which may be found in the accompanying
 * LICENSE file or <http://code.google.com/p/space-chronicles/>.
 */

package space.game.world;

import com.jogamp.opengl.util.texture.Texture;
import com.jogamp.opengl.util.texture.TextureIO;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GL3bc;
import javax.media.opengl.GLException;
import space.app.skyview.TextureMessage.Kind;
import space.model.Drawable;

/**
 * Draw a bounding box mase of counter clockwize quads
 * @author Pierre
 * Major Axis   Target                          sc      tc
 * +rx          TEXTURE_CUBE_MAP_POSITIVE_X     -rz     -ry
 * -rx          TEXTURE_CUBE_MAP_NEGATIVE_X     +rz     -ry
 * +ry          TEXTURE_CUBE_MAP_POSITIVE_Y     +rx     +rz
 * -ry          TEXTURE_CUBE_MAP_NEGATIVE_Y     +rx     -rz
 * +rz          TEXTURE_CUBE_MAP_POSITIVE_Z     +rx     -ry
 * -rz          TEXTURE_CUBE_MAP_NEGATIVE_Z     -rx     -ry
 */
public class WldSkyBox  {

    final boolean generateMipmap = false;
    final Texture facesTex[] = new Texture[6];
    final float cubeSize = 100;

    public WldSkyBox() throws IOException {

        for (int i = 0; i < facesTex.length; i++) {
            facesTex[i] = TextureIO.newTexture(
                    defaultTextureURL(),
                    generateMipmap, null);
        }
    }

    public WldSkyBox(URL boxUrl) throws IOException {
        load(boxUrl);
    }

    private URL defaultTextureURL() {
        return getClass().getResource("default.png");
    }

    public final void load(final URL boxUrl) {
        if (boxUrl == null) {
            throw new IllegalArgumentException();
        }
        try {
            final Properties properties = new Properties();
            {
                final InputStream is = boxUrl.openStream();
                properties.load(is);
                is.close();
            }
            final List<URL> tex = new ArrayList<URL>();
            for (String key : new String[]{"xn", "xp", "yn", "yp", "zn", "zp"}) {
                if (properties.containsKey(key)) {
                    tex.add(new URL(boxUrl, properties.getProperty(key)));
                } else {
                    tex.add(defaultTextureURL());
                }
            }
            load(tex.toArray(new URL[6]));
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    public final void load(URL facesFile[]) {
        for (int i = 0; i < facesFile.length; i++) {
            try {
                facesTex[i] = TextureIO.newTexture(facesFile[i], generateMipmap, null);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            } catch (GLException ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    public WldSkyBox(final URL facesFile[]) {
        load(facesFile);
    }

    public WldSkyBox(final String facesFile[]) throws IOException {
        for (int i = 0; i < facesFile.length; i++) {
            facesTex[i] = TextureIO.newTexture(getClass().getResource(
                    facesFile[i]), generateMipmap, null);
        }
    }

    public Drawable optimize(GL3bc gl) {
        throw new UnsupportedOperationException("Not supported yet.");


    }

    public void updateTexture(GL gl, Kind kind, byte[] texture) {
        try {
            Texture t = facesTex[kind.ordinal()];
            facesTex[kind.ordinal()] =
                    TextureIO.newTexture(new ByteArrayInputStream(texture),
                    generateMipmap, null);
            if (t != null) {
                t.destroy(gl);
            }

        } catch (IOException ex) {
            Logger.getLogger(WldSkyBox.class.getName()).log(Level.SEVERE, null,
                    ex);
        } catch (GLException ex) {
            Logger.getLogger(WldSkyBox.class.getName()).log(Level.SEVERE, null,
                    ex);
        }



    }
    /*public void setEnvMap(GL3bc gl) {
    gl.glTexParameteri(gl.GL_TEXTURE_CUBE_MAP, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR);
    gl.glTexParameteri(gl.GL_TEXTURE_CUBE_MAP, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR);
    gl.glTexParameteri(gl.GL_TEXTURE_CUBE_MAP, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE);
    gl.glTexParameteri(gl.GL_TEXTURE_CUBE_MAP, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE);
    gl.glTexParameteri(gl.GL_TEXTURE_CUBE_MAP, gl.GL_TEXTURE_WRAP_R, gl.GL_CLAMP_TO_EDGE);
    facesTex[0].updateImage(null);

    }*/

    public void drawDebug(GL3bc gl) {
        final float s = cubeSize;
        gl.glBegin(GL3bc.GL_QUADS);
        gl.glTexCoord2f(0.f, 0.f);
        gl.glVertex3f(+s, -s, +s);
        gl.glTexCoord2f(0.f, 1.f);
        gl.glVertex3f(+s, +s, +s);
        gl.glTexCoord2f(1.f, 1.f);
        gl.glVertex3f(+s, +s, -s);
        gl.glTexCoord2f(1.f, 0.f);
        gl.glVertex3f(+s, -s, -s);
        gl.glEnd();
        // NEGATIVE_X
        gl.glBegin(GL3bc.GL_QUADS);
        gl.glTexCoord2f(0.f, 0.f);
        gl.glVertex3f(-s, -s, -s);
        gl.glTexCoord2f(0.f, 1.f);
        gl.glVertex3f(-s, +s, -s);
        gl.glTexCoord2f(1.f, 1.f);
        gl.glVertex3f(-s, +s, +s);
        gl.glTexCoord2f(1.f, 0.f);
        gl.glVertex3f(-s, -s, +s);
        gl.glEnd();
        // POSITIVE_Y
        gl.glBegin(GL3bc.GL_QUADS);
        gl.glTexCoord2f(0.f, 0.f);
        gl.glVertex3f(-s, +s, +s);
        gl.glTexCoord2f(0.f, 1.f);
        gl.glVertex3f(-s, +s, -s);
        gl.glTexCoord2f(1.f, 1.f);
        gl.glVertex3f(+s, +s, -s);
        gl.glTexCoord2f(1.f, 0.f);
        gl.glVertex3f(+s, +s, +s);
        gl.glEnd();
        // NEGATIVE_Y
        gl.glBegin(GL3bc.GL_QUADS);
        gl.glTexCoord2f(0.f, 0.f);
        gl.glVertex3f(-s, -s, -s);
        gl.glTexCoord2f(0.f, 1.f);
        gl.glVertex3f(-s, -s, +s);
        gl.glTexCoord2f(1.f, 1.f);
        gl.glVertex3f(+s, -s, +s);
        gl.glTexCoord2f(1.f, 0.f);
        gl.glVertex3f(+s, -s, -s);
        gl.glEnd();
        // POSITIVE_Z
        gl.glBegin(GL3bc.GL_QUADS);
        gl.glTexCoord2f(0.f, 0.f);
        gl.glVertex3f(-s, -s, +s);
        gl.glTexCoord2f(0.f, 1.f);
        gl.glVertex3f(-s, +s, +s);
        gl.glTexCoord2f(1.f, 1.f);
        gl.glVertex3f(+s, +s, +s);
        gl.glTexCoord2f(1.f, 0.f);
        gl.glVertex3f(+s, -s, +s);
        gl.glEnd();
        // NEGATIVE_Z
        gl.glBegin(GL3bc.GL_QUADS);
        gl.glTexCoord2f(0.f, 0.f);
        gl.glVertex3f(+s, -s, -s);
        gl.glTexCoord2f(0.f, 1.f);
        gl.glVertex3f(+s, +s, -s);
        gl.glTexCoord2f(1.f, 1.f);
        gl.glVertex3f(-s, +s, -s);
        gl.glTexCoord2f(1.f, 0.f);
        gl.glVertex3f(-s, -s, -s);
        gl.glEnd();
        gl.glDisable(GL3bc.GL_TEXTURE_2D);

    }

    public void draw(GL3bc gl) {        
        gl.glEnable(gl.GL_TEXTURE_2D);
        gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL);
        gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_REPLACE);

        /**
         * This object never hides scene elements. Don't write to the z-buffer
         */
        gl.glDepthMask(false);
        final float s = cubeSize;

        // NEGATIVE_X
        if (facesTex[0] != null) {
            facesTex[0].bind();
            config(gl);
            gl.glBegin(GL3bc.GL_QUADS);
            gl.glTexCoord2f(0.f, 0.f);
            gl.glVertex3f(-s, -s, -s);
            gl.glTexCoord2f(0.f, 1.f);
            gl.glVertex3f(-s, +s, -s);
            gl.glTexCoord2f(1.f, 1.f);
            gl.glVertex3f(-s, +s, +s);
            gl.glTexCoord2f(1.f, 0.f);
            gl.glVertex3f(-s, -s, +s);
            gl.glEnd();
        }
        // POSITIVE_X
        if (facesTex[1] != null) {
            facesTex[1].bind();
            config(gl);
            gl.glBegin(GL3bc.GL_QUADS);
            gl.glTexCoord2f(0.f, 0.f);
            gl.glVertex3f(+s, -s, +s);
            gl.glTexCoord2f(0.f, 1.f);
            gl.glVertex3f(+s, +s, +s);
            gl.glTexCoord2f(1.f, 1.f);
            gl.glVertex3f(+s, +s, -s);
            gl.glTexCoord2f(1.f, 0.f);
            gl.glVertex3f(+s, -s, -s);
            gl.glEnd();
        }
        // NEGATIVE_Y
        if (facesTex[2] != null) {
            facesTex[2].bind();
            config(gl);
            gl.glBegin(GL3bc.GL_QUADS);
            gl.glTexCoord2f(0.f, 0.f);
            gl.glVertex3f(-s, -s, -s);
            gl.glTexCoord2f(0.f, 1.f);
            gl.glVertex3f(-s, -s, +s);
            gl.glTexCoord2f(1.f, 1.f);
            gl.glVertex3f(+s, -s, +s);
            gl.glTexCoord2f(1.f, 0.f);
            gl.glVertex3f(+s, -s, -s);
            gl.glEnd();
        }
        // POSITIVE_Y
        if (facesTex[3] != null) {
            facesTex[3].bind();
            config(gl);
            gl.glBegin(GL3bc.GL_QUADS);
            gl.glTexCoord2f(0.f, 0.f);
            gl.glVertex3f(-s, +s, +s);
            gl.glTexCoord2f(0.f, 1.f);
            gl.glVertex3f(-s, +s, -s);
            gl.glTexCoord2f(1.f, 1.f);
            gl.glVertex3f(+s, +s, -s);
            gl.glTexCoord2f(1.f, 0.f);
            gl.glVertex3f(+s, +s, +s);
            gl.glEnd();
        }
        // NEGATIVE_Z
        if (facesTex[4] != null) {
            facesTex[4].bind();
            config(gl);
            gl.glBegin(GL3bc.GL_QUADS);
            gl.glTexCoord2f(0.f, 0.f);
            gl.glVertex3f(+s, -s, -s);
            gl.glTexCoord2f(0.f, 1.f);
            gl.glVertex3f(+s, +s, -s);
            gl.glTexCoord2f(1.f, 1.f);
            gl.glVertex3f(-s, +s, -s);
            gl.glTexCoord2f(1.f, 0.f);
            gl.glVertex3f(-s, -s, -s);
            gl.glEnd();
        }
        // POSITIVE_Z
        if (facesTex[5] != null) {
            facesTex[5].bind();
            config(gl);
            gl.glBegin(GL3bc.GL_QUADS);
            gl.glTexCoord2f(0.f, 0.f);
            gl.glVertex3f(-s, -s, +s);
            gl.glTexCoord2f(0.f, 1.f);
            gl.glVertex3f(-s, +s, +s);
            gl.glTexCoord2f(1.f, 1.f);
            gl.glVertex3f(+s, +s, +s);
            gl.glTexCoord2f(1.f, 0.f);
            gl.glVertex3f(+s, -s, +s);
            gl.glEnd();
        }
        gl.glDisable(GL3bc.GL_TEXTURE_2D);
        gl.glDepthMask(true);
    }

    private void config(GL3bc gl) {
        //This will clamp filtering to the edge the texture, eliminating
        //interference from border texels.
        gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR);
        gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_NEAREST);
        gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE);
        gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE);
    }

    public void dispose(GL3bc gl) {
    }
}
